﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Assignment_3.Raytracer;
using Assignment_3.Scene;
using Assignment_3.GenericStructures;
using Assignment_3.Scene.Basic_Shapes;
using System.IO;
using Microsoft.Win32;
using System.Xml.Linq;

namespace Assignment_3
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        SceneComposition scene;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void runRayTracer_Click(object sender, RoutedEventArgs e)
        {
            if (scene == null)
            {
                Open_Click(null,null);
            }

            Raytracer.Raytracer raytracer = new Raytracer.Raytracer((int)ImageDisplay.Width, (int)ImageDisplay.Height, scene, 10);

            raytracer.run(SuperSamplingLevel());

            Ray[,] rays = raytracer.rays;

//            ColorVector color = rays[(int)(ImageDisplay.Width / 2), (int)(ImageDisplay.Height / 2)].color;

            displayImage(rays);
        }

        private void displayImage(Ray[,] rays)
        {

            WriteableBitmap bitmap = new WriteableBitmap((int)ImageDisplay.Width, (int)ImageDisplay.Height, 96, 96, PixelFormats.Bgra32, null);
            byte[] bytes = new byte[rays.Length*4];
            //list of bytes r,g,b,a for each pixel

                        for (int j = 0; j < ImageDisplay.Height; j++)
            {
                for (int i = 0; i < ImageDisplay.Width; i++)
                {
                    bytes[(j * (int)ImageDisplay.Width + i) * 4 + 0] = (byte)Math.Round(rays[i, j].color.blue * 255);
                    bytes[(j * (int)ImageDisplay.Width + i) * 4 + 1] = (byte)Math.Round(rays[i, j].color.green * 255);
                    bytes[(j * (int)ImageDisplay.Width + i) * 4 + 2] = (byte)Math.Round(rays[i, j].color.red * 255);
                    bytes[(j * (int)ImageDisplay.Width + i) * 4 + 3] = (byte)255;
                }
            }
            int bpp = (bitmap.Format.BitsPerPixel + 7) / 8;
            bitmap.WritePixels(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight), bytes, bitmap.PixelWidth*bpp, 0);

            ImageDisplay.Source = bitmap;

            //now should just need to display.
        }

        

        private void Open_Click(object sender, RoutedEventArgs e)
        {
            //update scene
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.ShowDialog();
            if (dialog.CheckFileExists)
            {
                string path = dialog.FileName;

                XDocument xmlSceneDocument = XDocument.Load(path);

                XElement xmlScene = xmlSceneDocument.Element("scene");

                scene = new SceneComposition();
                //load scene properties         
                XElement properties = xmlScene.Element("properties");
                scene.sceneWidth = Int32.Parse(properties.Descendants("width").ElementAt(0).Value);
                scene.sceneHeight = Int32.Parse(properties.Descendants("height").ElementAt(0).Value);
                scene.ambientLightingCoefficient = Double.Parse(properties.Descendants("ambient").ElementAt(0).Value);
                scene.indexOfRefraction = Double.Parse(properties.Element("refraction").Value);

                foreach (var pointLight in xmlScene.Element("lights").Descendants("pointlight"))
                {
                    double x = double.Parse(pointLight.Element("coord").Element("x").Value);
                    double y = double.Parse(pointLight.Element("coord").Element("y").Value);
                    double z = double.Parse(pointLight.Element("coord").Element("z").Value);

                    double red = double.Parse(pointLight.Element("color").Element("red").Value);
                    double green = double.Parse(pointLight.Element("color").Element("red").Value);
                    double blue = double.Parse(pointLight.Element("color").Element("red").Value);

                    PointLight temp = new PointLight(new Assignment_3.GenericStructures.Vector(x,y,z),new ColorVector(red,green,blue));

                    scene.addLight(temp);
                }

                //add spheres
                foreach (var xmlSphere in xmlScene.Element("shapes").Descendants("sphere"))
                {
                    double x = double.Parse(xmlSphere.Element("center").Element("x").Value);
                    double y = double.Parse(xmlSphere.Element("center").Element("y").Value);
                    double z = double.Parse(xmlSphere.Element("center").Element("z").Value);

                    double radius = double.Parse(xmlSphere.Element("radius").Value);

                    Sphere tempSphere = new Sphere(new GenericStructures.Vector(x, y, z), radius);
                    tempSphere.properties = new ShapeProperties();

                    XElement shapeProperties = xmlSphere.Element("properties");
                    //load in the shape properties
                    tempSphere.properties.indexOfRefraction = double.Parse(shapeProperties.Element("refraction").Value);
                    tempSphere.properties.reflectionCoefficient = double.Parse(shapeProperties.Element("reflection").Value);
                    tempSphere.properties.transparencyCoefficient = double.Parse(shapeProperties.Element("transparency").Value);
                    tempSphere.properties.shininessCoefficient = double.Parse(shapeProperties.Element("shininess").Value);
                    tempSphere.properties.coefficientSpecularReflection = double.Parse(shapeProperties.Element("specular").Value);
                    tempSphere.properties.coefficientDiffuseReflection = double.Parse(shapeProperties.Element("diffuse").Value);

                    double red = double.Parse(shapeProperties.Element("color").Element("red").Value);
                    double green = double.Parse(shapeProperties.Element("color").Element("green").Value);
                    double blue = double.Parse(shapeProperties.Element("color").Element("blue").Value);

                    tempSphere.properties.color = new ColorVector(red, green, blue);

                    scene.addShape(tempSphere);
                }

                //add triangles
                foreach (var xmlTriangle in xmlScene.Element("shapes").Descendants("triangle"))
                {
                    List<GenericStructures.Vector> points = new List<GenericStructures.Vector>();

                    foreach(var xmlPoint in xmlTriangle.Elements("point"))
                    {
                        double x = double.Parse(xmlPoint.Element("x").Value);
                        double y = double.Parse(xmlPoint.Element("y").Value);
                        double z = double.Parse(xmlPoint.Element("z").Value);

                        GenericStructures.Vector toAdd = new Assignment_3.GenericStructures.Vector(x, y, z);

                        points.Add(toAdd);
                    }

                    Triangle tempTriangle = new Triangle(points[0],points[1],points[2]);
                    tempTriangle.properties = new ShapeProperties();

                    XElement shapeProperties = xmlTriangle.Element("properties");
                    //load in the shape properties
                    tempTriangle.properties.indexOfRefraction = double.Parse(shapeProperties.Element("refraction").Value);
                    tempTriangle.properties.reflectionCoefficient = double.Parse(shapeProperties.Element("reflection").Value);
                    tempTriangle.properties.transparencyCoefficient = double.Parse(shapeProperties.Element("transparency").Value);
                    tempTriangle.properties.shininessCoefficient = double.Parse(shapeProperties.Element("shininess").Value);
                    tempTriangle.properties.coefficientSpecularReflection = double.Parse(shapeProperties.Element("specular").Value);
                    tempTriangle.properties.coefficientDiffuseReflection = double.Parse(shapeProperties.Element("diffuse").Value);

                    double red = double.Parse(shapeProperties.Element("color").Element("red").Value);
                    double green = double.Parse(shapeProperties.Element("color").Element("green").Value);
                    double blue = double.Parse(shapeProperties.Element("color").Element("blue").Value);

                    tempTriangle.properties.color = new ColorVector(red, green, blue);

                    scene.addShape(tempTriangle);
                }

                //load quads
                foreach (var xmlQuad in xmlScene.Element("shapes").Descendants("quad"))
                {
                    List<GenericStructures.Vector> points = new List<GenericStructures.Vector>();

                    foreach (var xmlPoint in xmlQuad.Elements("point"))
                    {
                        double x = double.Parse(xmlPoint.Element("x").Value);
                        double y = double.Parse(xmlPoint.Element("y").Value);
                        double z = double.Parse(xmlPoint.Element("z").Value);

                        GenericStructures.Vector toAdd = new Assignment_3.GenericStructures.Vector(x, y, z);

                        points.Add(toAdd);
                    }

                    Quad tempQuad = new Quad(points[0], points[1], points[2], points[3]);
                    tempQuad.properties = new ShapeProperties();

                    XElement shapeProperties = xmlQuad.Element("properties");
                    //load in the shape properties
                    tempQuad.properties.indexOfRefraction = double.Parse(shapeProperties.Element("refraction").Value);
                    tempQuad.properties.reflectionCoefficient = double.Parse(shapeProperties.Element("reflection").Value);
                    tempQuad.properties.transparencyCoefficient = double.Parse(shapeProperties.Element("transparency").Value);
                    tempQuad.properties.shininessCoefficient = double.Parse(shapeProperties.Element("shininess").Value);
                    tempQuad.properties.coefficientSpecularReflection = double.Parse(shapeProperties.Element("specular").Value);
                    tempQuad.properties.coefficientDiffuseReflection = double.Parse(shapeProperties.Element("diffuse").Value);

                    double red = double.Parse(shapeProperties.Element("color").Element("red").Value);
                    double green = double.Parse(shapeProperties.Element("color").Element("green").Value);
                    double blue = double.Parse(shapeProperties.Element("color").Element("blue").Value);

                    tempQuad.properties.color = new ColorVector(red, green, blue);

                    scene.addShape(tempQuad);
                }
            }
        }

        private void Assignment3_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            toolbar1.Width = e.NewSize.Width;
            if (e.PreviousSize.Width == 0 || e.PreviousSize.Height == 0)
            {
                return;
            }
            ImageDisplay.Width = ImageDisplay.Width + (e.NewSize.Width - e.PreviousSize.Width);
            ImageDisplay.Height = ImageDisplay.Height + (e.NewSize.Height - e.PreviousSize.Height);
            GridLayoutThingy.Width = GridLayoutThingy.Width + (e.NewSize.Width - e.PreviousSize.Width);
            GridLayoutThingy.Height = GridLayoutThingy.Height + (e.NewSize.Height - e.PreviousSize.Height);
        }

        private int SuperSamplingLevel()
        {
            switch (Supersampling_Level.SelectedIndex)
            {
                case 0:
                    return 1;
                case 1:
                    return 4;
                case 2:
                    return 9;
                case 3:
                    return 16;
                case 4:
                    return 25;
                default:
                    return 1;
            }
        }

        private void RenderToFile_Click(object sender, RoutedEventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.AddExtension = true;
            dialog.DefaultExt = "jpg";
            dialog.ShowDialog();


            if (dialog.FileName != "")
            {
                string path = dialog.FileName;

                if (scene == null)
                {
                    Open_Click(null, null);
                }

                int outputWidth = (int)Int32.Parse(imgWidth.Text);
                int outputHeight = (int)Int32.Parse(imgHeight.Text);

                Raytracer.Raytracer raytracer = new Raytracer.Raytracer(outputWidth, outputHeight, scene, 10);

                raytracer.run(SuperSamplingLevel());

                Ray[,] rays = raytracer.rays;

                WriteableBitmap bitmap = new WriteableBitmap(outputWidth, outputHeight, 96, 96, PixelFormats.Bgra32, null);
                byte[] bytes = new byte[rays.Length * 4];
                //list of bytes r,g,b,a for each pixel

                for (int j = 0; j < outputHeight; j++)
                {
                    for (int i = 0; i < outputWidth; i++)
                    {
                        bytes[(j * (int)outputWidth + i) * 4 + 0] = (byte)Math.Round(rays[i, j].color.blue * 255);
                        bytes[(j * (int)outputWidth + i) * 4 + 1] = (byte)Math.Round(rays[i, j].color.green * 255);
                        bytes[(j * (int)outputWidth + i) * 4 + 2] = (byte)Math.Round(rays[i, j].color.red * 255);
                        bytes[(j * (int)outputWidth + i) * 4 + 3] = (byte)255;
                    }
                }
                int bpp = (bitmap.Format.BitsPerPixel + 7) / 8;
                bitmap.WritePixels(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight), bytes, bitmap.PixelWidth * bpp, 0);

                FileStream stream = new FileStream(path, FileMode.Create);
                JpegBitmapEncoder encoder = new JpegBitmapEncoder();
                encoder.QualityLevel = 100;
                encoder.Frames.Add(BitmapFrame.Create(bitmap));
                encoder.Save(stream);
                stream.Close();
            }
        }


    }
}
